package com.unicom.config;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import com.unicom.network.Command;
import com.unicom.network.IRunable;
import com.unicom.network.IStoryProtocol;
import com.unicom.network.JSONProtocol;
import com.unicom.network.task.TaskGetComment;
import com.unicom.utils.StoryListener;

import android.content.Context;
import android.os.Process;

public class StoryCore implements IStoryCore, IStoryConfig, Runnable {
	
	public static final int PRIORITY_BACKGROUND = 1;
	public static final int PRIORITY_LOW = 2;
	public static final int PRIORITY_MEDIUM = 3;
	public static final int PRIORITY_HIGH = 4;
	public static final int PRIORITY_TOP = 5;
	
	private static StoryCore instance = null;
	private Context mContext;
	private IStoryProtocol protocol;
	private static Set<StoryListener> mListener = new CopyOnWriteArraySet<StoryListener>();
	boolean coreFlag = false;
	private BlockingQueue<Command> mCommands = new PriorityBlockingQueue<Command>();
	private boolean connected = true;
	private byte[] connectionLock = new byte[0];
	public int sleepTime = 10 * 1000;
	private Thread mThread;
	
	public static AtomicInteger sequencing = new AtomicInteger(0);
	
	/*
	 * construstor
	 */
	public StoryCore(Context context) {
		this.mContext = context;
		this.protocol = new JSONProtocol(context, this);
		mThread = new Thread(this);
		mThread.setName("STORYCORE");
		mThread.start();
	}
	
	public synchronized static StoryCore getInstance(Context context) {
		if (instance == null) {
			instance = new StoryCore(context);
		}
		return instance;
	}
	
	public void addListener(StoryListener listener) {
		if (listener != null) {
			mListener.add(listener);
		}
	}

	public void removeListener(StoryListener listener) {
		mListener.remove(listener);
	}

	@Override
	public Context getContext() {
		return mContext;
	}

	@Override
	public IStoryProtocol getProtocol() {
		return protocol;
	}

	@Override
	public String getAuthenKey() {
		return "";
	}
	
	@Override
	public Set<StoryListener> copyAndReconstructListeners(StoryListener listener) {
		if (listener == null) {
			return mListener;
		}
		Set<StoryListener> listeners = new HashSet<StoryListener>(mListener);
		listeners.add(listener);
		return listeners;
	}
	
	public void put(StoryListener listener, String action, int priority,IRunable runable, boolean sendByUI) {
		putCommand(mCommands, action, listener, runable, priority, sendByUI,Command.DEFAULT_RETRY_TIMES);
	}
	
	private void putCommand(BlockingQueue<Command> queue, String action,
			StoryListener listener, IRunable runnable, int priority,
			boolean sendByUI, int retryTimes) {
		int retries = 10;
		Exception e = null;
		while (retries-- > 0) {
			try {

				Command command = new Command(runnable, listener, action,
						priority, sendByUI);
				queue.put(command);
				return;
			} catch (InterruptedException ie) {
				try {
					Thread.sleep(IStoryConfig.PUT_COMMAND_RETRY_INTERVAL);
				} catch (InterruptedException ne) {
				}
				e = ie;
			}
		}
		throw new Error(e);
	}

	@Override
	public void run() {
		Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
		coreFlag = true;
		while (coreFlag) {
			String commandDescription = null;
			try {
				final Command command = mCommands.take();
				if(!connected){
					if(command != null){
						if(command.sendByUI){
							command.runable.timeoutCallback();
							continue;
						} else {
							mCommands.add(command);
						}
					}
					synchronized(connectionLock){
						connectionLock.wait(NETWORK_FAIL_WAIT);
						continue;
					}
				}
				if(command != null){
					commandDescription = command.action;
					try {
						command.runable.run();
						resetSleepTime();
					} catch (Exception e) {
						if (command.sendByUI) {
							command.runable.timeoutCallback();
						} else {
							if (command.retryTime > 0) {
								command.retryTime--;
								new Thread() {
									@Override
									public void run() {
										try {
											sleep(increaseSleepTime());
											mCommands.put(command);
										} catch (InterruptedException e) {
										}
									}

								}.start();
							}
						}
					}
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
		
	}
	
	private void resetSleepTime() {
		sleepTime = 10 * 1000;
	}
	private int increaseSleepTime() {
		if (sleepTime == 10 * 1000) {
			// 30 seconds
			sleepTime = 30 * 1000;
		} else if (sleepTime == 30 * 1000) {
			// 3 minutes
			sleepTime = 3 * 60 * 1000;
		} else if (sleepTime == 3 * 60 * 1000) {
			// 5 minutes
			sleepTime = 5 * 60 * 1000;
		}
		return sleepTime;
	}
	
	
	@Override
	public int taskGetComment(String authenKey, String Schedule_id,
			String pageLimit, String pageNumber, StoryListener listener) {
		TaskGetComment taskGetComment = new TaskGetComment(listener, instance,
				authenKey, Schedule_id, pageLimit, pageNumber);
		put(listener, taskGetComment.getClass().getName(), PRIORITY_MEDIUM,
				taskGetComment, true);
		return 0;
	}

}
